package com.ruoyi.pointRecord.service.impl;

import com.ruoyi.address.domain.UserAddressInfo;
import com.ruoyi.address.mapper.UserAddressInfoMapper;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.CodeGeneratorUtil;
import com.ruoyi.pointRecord.domain.PointsExchangeRecord;
import com.ruoyi.pointRecord.mapper.PointsExchangeRecordMapper;
import com.ruoyi.pointRecord.service.IProductExchangeService;
import com.ruoyi.points.dao.IPointsChangeRecordDao;
import com.ruoyi.product.domain.PointsProductInfo;
import com.ruoyi.product.mapper.PointsProductInfoMapper;
import com.ruoyi.system.domain.UserAdditionalInfo;
import com.ruoyi.system.mapper.UserAdditionalInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;

@Service
@Slf4j
public class ProductExchangeServiceImpl implements IProductExchangeService {

    @Autowired
    private PointsProductInfoMapper pointsProductInfoMapper;

    @Autowired
    private PointsExchangeRecordMapper pointsExchangeRecordMapper;

    @Autowired
    private UserAddressInfoMapper userAddressInfoMapper;

    @Autowired
    private IPointsChangeRecordDao pointsChangeRecordDao;

    @Autowired
    private UserAdditionalInfoMapper userAdditionalInfoMapper;

    @Autowired
    @Qualifier("numberRedisTemplate")
    private RedisTemplate<String, String> numberRedisTemplate;

    @Autowired
    private DefaultRedisScript<Long> stockDeductScript;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public AjaxResult exchangeProduct(PointsExchangeRecord pointsExchangeRecord) {
        Long userId = pointsExchangeRecord.getUserId();
        Long productId = pointsExchangeRecord.getProductId();
        Long exchangeQuantity = pointsExchangeRecord.getExchangeQuantity();
        String remark = pointsExchangeRecord.getRemark();

        // 参数校验
        if (exchangeQuantity == null || exchangeQuantity <= 0) {
            return AjaxResult.error("兑换数量必须大于0");
        }

        // Redis分布式锁key
        String lockKey = "product_exchange_lock:" + productId;
        String stockKey = "product_stock:" + productId;

        // 获取分布式锁，防止并发 - 统一使用numberRedisTemplate
        Boolean lockAcquired = numberRedisTemplate.opsForValue()
                .setIfAbsent(lockKey, "1", Duration.ofSeconds(10));
        if (!lockAcquired) {
            return AjaxResult.error("系统繁忙，请稍后重试");
        }

        try {
            // 1. 查询商品
            PointsProductInfo product = pointsProductInfoMapper.selectPointsProductInfoById(productId);
            if (product == null) {
                return AjaxResult.error("商品不存在");
            }

            // 2. 校验商品状态和有效期
            validateProductStatus(product);
            validateProductPeriod(product);
            validateProductLimit(product, userId, exchangeQuantity);

            // 3. 初始化Redis库存（如果不存在）- 统一使用numberRedisTemplate
            initProductStockInRedis(stockKey, product.getStockQuantity());

            // 4. 使用Lua脚本原子性扣减库存 - 统一使用numberRedisTemplate
            Long deductResult;
            try {
                deductResult = numberRedisTemplate.execute(
                        stockDeductScript,
                        Collections.singletonList(stockKey),
                        exchangeQuantity.toString()
                );
            } catch (Exception e) {
                log.error("执行库存扣减脚本失败: productId={}, quantity={}", productId, exchangeQuantity, e);
                return AjaxResult.error("系统异常，请稍后重试");
            }

            // 处理脚本执行结果
            if (deductResult == -2) {
                return AjaxResult.error("参数错误");
            } else if (deductResult == -1) {
                return AjaxResult.error("库存信息异常");
            } else if (deductResult == 0) {
                return AjaxResult.error("库存不足");
            } else if (deductResult != 1) {
                return AjaxResult.error("库存操作失败，未知返回值：" + deductResult);
            }

            // 5. 校验用户积分
            Long totalPointsCost = product.getPointsPrice() * exchangeQuantity;
            UserAdditionalInfo userInfo = userAdditionalInfoMapper.selectUserAdditionalInfoByUserId(userId);
            if (userInfo == null || userInfo.getCurrentPoints() < totalPointsCost) {
                // 回滚Redis库存
                rollbackStock(stockKey, exchangeQuantity);
                return AjaxResult.error("积分不足，当前积分：" +
                        (userInfo != null ? userInfo.getCurrentPoints() : 0) + "，需要积分：" + totalPointsCost);
            }

            // 6. 查询默认地址
            UserAddressInfo addressInfo = getUserDefaultAddress(userId);
            if (addressInfo == null) {
                rollbackStock(stockKey, exchangeQuantity);
                return AjaxResult.error("请先设置默认收货地址");
            }

            // 7. 扣减积分
            String batchCode = CodeGeneratorUtil.generateBatchCode();
            int pointsResult = pointsChangeRecordDao.recordAndUpdatePoints(userId, -totalPointsCost.longValue(), 7L, batchCode);
            if (pointsResult <= 0) {
                rollbackStock(stockKey, exchangeQuantity);
                return AjaxResult.error("积分扣减失败");
            }

            // 8. 更新数据库商品库存和销量
            updateProductStock(productId, exchangeQuantity);

            // 9. 创建兑换记录
            PointsExchangeRecord exchangeRecord = createExchangeRecord(userId, product, exchangeQuantity, totalPointsCost, addressInfo, remark, batchCode);
            int recordResult = pointsExchangeRecordMapper.insertPointsExchangeRecord(exchangeRecord);
            if (recordResult <= 0) {
                throw new RuntimeException("创建兑换记录失败");
            }

            return AjaxResult.success("兑换成功", exchangeRecord.getExchangeCode());

        } catch (Exception e) {
            // 异常时回滚Redis库存
            rollbackStock(stockKey, exchangeQuantity);
            log.error("商品兑换异常: productId={}, userId={}", productId, userId, e);
            throw e;
        } finally {
            // 释放分布式锁 - 统一使用numberRedisTemplate
            try {
                numberRedisTemplate.delete(lockKey);
            } catch (Exception e) {
                log.warn("释放分布式锁失败: lockKey={}", lockKey, e);
            }
        }
    }

    /**
     * 初始化商品在Redis中的库存 - 修改为使用numberRedisTemplate
     */
    private void initProductStockInRedis(String stockKey, Long stock) {
        try {
            Boolean exists = numberRedisTemplate.hasKey(stockKey);
            if (!exists) {
                numberRedisTemplate.opsForValue().set(stockKey, stock.toString(), Duration.ofHours(24));
                log.info("初始化Redis库存: key={}, stock={}", stockKey, stock);
            }
        } catch (Exception e) {
            log.error("初始化Redis库存失败: key={}, stock={}", stockKey, stock, e);
            throw new RuntimeException("库存初始化失败", e);
        }
    }

    /**
     * 回滚库存 - 修改为使用numberRedisTemplate
     */
    private void rollbackStock(String stockKey, Long quantity) {
        try {
            numberRedisTemplate.opsForValue().increment(stockKey, quantity);
            log.info("回滚Redis库存: key={}, quantity={}", stockKey, quantity);
        } catch (Exception e) {
            log.error("回滚Redis库存失败: key={}, quantity={}", stockKey, quantity, e);
        }
    }

    /**
     * 获取用户默认地址信息
     * @param userId 用户ID
     * @return 默认地址信息
     */
    private UserAddressInfo getUserDefaultAddress(Long userId) {
        return userAddressInfoMapper.selectUserAddressInfoByUserId(userId);
    }

    /**
     * 校验商品状态和库存
     */
    private void validateProductStatus(PointsProductInfo product) {
        if (product.getProductStatus() != 1) {
            throw new RuntimeException("商品已下架或售罄");
        }
    }

    private void validateProductPeriod(PointsProductInfo product) {
        LocalDate now = LocalDate.now();
        if (product.getStartTime() != null && now.isBefore(product.getStartTime())) {
            throw new RuntimeException("商品尚未上架");
        }
        if (product.getEndTime() != null && now.isAfter(product.getEndTime())) {
            throw new RuntimeException("商品已过期");
        }
    }

    private void validateProductStock(PointsProductInfo product, Long exchangeQuantity) {
        if (product.getStockQuantity() < exchangeQuantity) {
            throw new RuntimeException("库存不足，当前库存：" + product.getStockQuantity());
        }
    }

    private void validateProductLimit(PointsProductInfo product, Long userId, Long exchangeQuantity) {
        if (product.getExchangeLimit() > 0) {
            Long userExchangedCount = pointsExchangeRecordMapper.countUserExchangedQuantity(userId, product.getId());
            if (userExchangedCount + exchangeQuantity > product.getExchangeLimit()) {
                throw new RuntimeException("超出限购数量，每人限购：" + product.getExchangeLimit() + "件");
            }
        }
    }

    /**
     * 创建兑换记录
     */
    private PointsExchangeRecord createExchangeRecord(Long userId, PointsProductInfo product,
                                                      Long exchangeQuantity, Long totalPointsCost,
                                                      UserAddressInfo addressInfo, String remark, String batchCode) {
        PointsExchangeRecord record = new PointsExchangeRecord();

        // 基本信息
        record.setExchangeCode(CodeGeneratorUtil.generateCode());
        record.setUserId(userId);
        record.setProductId(product.getId());

        // 从商品表填充的字段
        record.setProductName(product.getProductName());
        record.setProductImage(product.getProductImage());
        record.setPointsCost(Long.valueOf(totalPointsCost));
        record.setExchangeQuantity(Long.valueOf(exchangeQuantity));

        // 从默认地址表填充的字段
        record.setDeliveryAddress(addressInfo.getAddress());
        record.setContactPhone(addressInfo.getContactPhone());

        // 其他信息
        record.setRemark(remark);

        // 状态和时间
        record.setExchangeStatus(1L); // 待发货
        record.setExchangeTime(LocalDate.from(LocalDateTime.now()));

        // 审计字段
        record.setCreatedTime(LocalDate.from(LocalDateTime.now()));
        record.setDeletedFlag(0L);

        return record;
    }

    /**
     * 更新商品库存和销量
     */
    private void updateProductStock(Long productId, Long exchangeQuantity) {
        int result = pointsProductInfoMapper.updateStockAndSales(productId, exchangeQuantity);
        if (result <= 0) {
            throw new RuntimeException("更新商品库存失败");
        }
    }
}